
//Dep依赖收集系统，用于Vue.js或类似框架中的响应式系统。
//它允许对象（Dep实例）被观察，并且当这些对象发生变化时，
//可以通知所有订阅这些变化的观察者（DepTarget实例）。
//这个过程涉及到依赖收集（depend方法）和通知更新（notify方法）。
//此外，代码还提供了用于管理当前正在评估的目标观察者的栈（targetStack），
//以及用于清理不再需要的订阅者的机制（cleanupDeps函数和相关逻辑）。

// 引入配置文件
import config from '../config';
// 引入调试器选项和调试器事件额外信息的类型定义
import { DebuggerOptions, DebuggerEventExtraInfo } from 'v3';

// 初始化一个用于生成Dep实例唯一标识符的变量，从0开始递增
let uid = 0;

// 定义一个数组，用于存储待清理的Dep实例
const pendingCleanupDeps: Dep[] = [];

// 定义一个函数，用于清理待清理的Dep实例
export const cleanupDeps = () => {
  for (let i = 0; i < pendingCleanupDeps.length; i++) {
    const dep = pendingCleanupDeps[i];
    // 过滤掉null的订阅者
    dep.subs = dep.subs.filter(s => s);
    // 重置待清理状态
    dep._pending = false;
  }
  // 清空待清理数组
  pendingCleanupDeps.length = 0;
};

// 定义一个接口，用于描述DepTarget，它扩展了DebuggerOptions并添加了id、addDep和update方法
export interface DepTarget extends DebuggerOptions {
  id: number;
  addDep(dep: Dep): void;
  update(): void;
}

// 定义一个Dep类，表示一个可以被多个指令订阅的可观察对象
export default class Dep {
  // 静态属性，用于存储当前正在评估的目标观察者，全局唯一
  static target?: DepTarget | null;
  // Dep实例的唯一标识符
  id: number;
  // 存储订阅者的数组
  subs: Array<DepTarget | null>;
  // 标记是否有待清理的订阅者
  _pending = false;

  constructor() {
    this.id = uid++; // 分配唯一标识符
    this.subs = []; // 初始化订阅者数组
  }

  // 添加订阅者
  addSub(sub: DepTarget) {
    this.subs.push(sub);
  }

  // 移除订阅者，将其置为null并标记为待清理
  removeSub(sub: DepTarget) {
    this.subs[this.subs.indexOf(sub)] = null;
    if (!this._pending) {
      this._pending = true;
      pendingCleanupDeps.push(this); // 将自己添加到待清理数组
    }
  }

  // 依赖收集，当Dep.target存在时，将自己添加到Dep.target的依赖中
  depend(info?: DebuggerEventExtraInfo) {
    if (Dep.target) {
      Dep.target.addDep(this);
      if (__DEV__ && info && Dep.target.onTrack) {
        Dep.target.onTrack({
          effect: Dep.target,
          ...info
        });
      }
    }
  }

  // 通知所有订阅者更新
  notify(info?: DebuggerEventExtraInfo) {
    const subs = this.subs.filter(s => s) as DepTarget[]; // 稳定订阅者列表，过滤掉null
    if (__DEV__ && !config.async) {
      subs.sort((a, b) => a.id - b.id); // 如果不是异步运行，则对订阅者进行排序以确保正确的触发顺序
    }
    for (let i = 0, l = subs.length; i < l; i++) {
      const sub = subs[i];
      if (__DEV__ && info) {
        sub.onTrigger &&
          sub.onTrigger({
            effect: subs[i],
            ...info
          });
      }
      sub.update(); // 调用订阅者的update方法
    }
  }
}

// 当前正在评估的目标观察者，全局唯一，因为一次只能评估一个观察者
Dep.target = null;
// 定义一个栈，用于存储目标观察者
const targetStack: Array<DepTarget | null | undefined> = [];

// 将目标观察者推入栈中，并设置Dep.target为当前目标
export function pushTarget(target?: DepTarget | null) {
  targetStack.push(target);
  Dep.target = target;
}

// 将目标观察者从栈中弹出，并更新Dep.target为新的栈顶目标
export function popTarget() {
  targetStack.pop();
  Dep.target = targetStack[targetStack.length - 1];
}